package com.gitee.wsl.mathematics.vector.vec2

import com.gitee.wsl.ext.array.DoubleArray
import com.gitee.wsl.io.dataproxy.DataGet
import com.gitee.wsl.io.dataproxy.asProxy
import com.gitee.wsl.mathematics.coordinate.d2.Coordinate2D
import com.gitee.wsl.mathematics.level.matrix.CoordinateMatrix2D
import com.gitee.wsl.mathematics.vector.vec3.Vec3
import com.gitee.wsl.mathematics.vector.vec4.Vec4


/**
 * A class representing a two-dimensional / planar vector.
 */
open class Vec2(override val proxy: DataGet<Double>):
    Coordinate2D<Vec2> {
    constructor(x: Double=0.0, y: Double=0.0):this(DoubleArray(x,y).asProxy())
    constructor(a: Number) : this(a.toDouble(), a.toDouble())
    constructor(x: Number, y: Number) : this(x.toDouble(), y.toDouble())
    constructor(base: Vector2<*,*>):this(base.x, base.y)

    override val self
        get() = this

    override val vec2 get() = this
    override val vec3 get() = Vec3(x, y, 0.0)
    override val vec4 get() = Vec4(x, y, 0.0, 0.0)

    override fun create(x: Double, y: Double): Vec2 = Vec2(x,y)

    companion object {
        val right get()     = Vec2(1.0, 0.0)
        val left get()      = Vec2(-1.0, 0.0)
        val up get()        = Vec2(0.0, 1.0)
        val down get()      = Vec2(0.0, -1.0)
        val one get()       = Vec2(1.0, 1.0)
        val zero get()      = Vec2(0.0, 0.0)
        val negativeInfinity get() = Vec2(Double.NEGATIVE_INFINITY)
        val positiveInfinity get() = Vec2(Double.POSITIVE_INFINITY)
    }

    override fun toString(): String {
        val xInt = x.toInt();
        val yInt = y.toInt()
        val xs = if (x == xInt.toDouble()) xInt.toString() else x.toString()
        val ys = if (y == yInt.toDouble()) yInt.toString() else y.toString()

        return "[$xs, $ys]"
    }

    override fun equals(other: Any?) = other is Vec2 && x == other.x && y == other.y
    override fun hashCode() = 31 * x.hashCode() + y.hashCode()

 }


internal fun Vec2.Companion.read2DPoints(input: String): Set<Vec2> {
    val points = mutableSetOf<Vec2>()
    val inputs = input.split("\\*".toRegex()).dropLastWhile { it.isEmpty() }

    for (s0 in inputs) {
        val s = s0.trim()
        if (s.isEmpty()) continue

        if (s.startsWith("(") && s.endsWith("]"))
            points.addAll(CoordinateMatrix2D.fromString(s).coordinates)
        else
            points.add(Vec2.fromString(s))
    }

    return points
}

/**
 * Parses a 2D Coordinate from a string.
 * @param point String
 * @return 2D Coordinate
 */
fun Vec2.Companion.fromString(point: String): Vec2 {
    val point0 = point.trim { it <= ' ' }.replace("[\\[\\]\\s]".toRegex(), "")
    val parts = point0.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
    if (parts.size != 2) throw IllegalArgumentException("Invalid Coordinate Length")

    val x = parts[0].toDouble(); val y = parts[1].toDouble()
    return Vec2(x, y)
}
